#include "Command.h"
#include <iostream>
#include <sstream>
#include <regex>

Command::Command(Database& db) : m_db(db) {}

void Command::executeCommand(const std::string& command) {
    try {
        if (parseCreateDatabaseCommand(command)) {
            return;
        }
        else if (parseDropDatabaseCommand(command)) {
            return;
        }
        else if (parseUseDatabaseCommand(command)) {
            return;
        }
        else if (parseCreateTableCommand(command)) {
            return;
        }
        else if (parseDropTableCommand(command)) {
            return;
        }
        else if (parseInsertCommand(command)) {
            return;
        }
        else if (parseSelectCommand(command)) {
            return;
        }
        else if (parseUpdateCommand(command)) {
            return;
        }
        else if (parseDeleteCommand(command)) {
            return;
        }
        else if (parseAlterTableCommand(command)) {
            return;
        }
        else {
            std::cout << "Invalid command." << std::endl;
        }
    }
    catch (const std::exception& e) {
        std::cout << "Caught exception: " << e.what() << std::endl;
    }
}

void printValues(const std::vector<std::vector<Value>>& values) {
    for (const auto& row : values) {
        for (const auto& value : row) {
            std::cout << value.toString() << " | ";
        }
        std::cout << std::endl;
    }
}

bool Command::parseCreateDatabaseCommand(const std::string& command) {
    std::regex regex("^\\s*CREATE\\s+DATABASE\\s+(\\w+)\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string dbName = match[1].str();
        bool success = Database::createDatabase(dbName);
        if (success) {
            m_db.logUserCommand(command);
            m_db.logUserCommand("Database created successfully.");
            std::cout << "Database '" << dbName << "' created successfully." << std::endl;
        }
        else {
            m_db.logUserCommand("Failed to create database");
            std::cout << "Failed to create database '" << dbName << "'." << std::endl;
        }
        return true;
    }
    return false;
}

bool Command::parseDropDatabaseCommand(const std::string& command) {
    std::regex regex("^\\s*DROP\\s+DATABASE\\s+(\\w+)\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string dbName = match[1].str();
        bool success = Database::dropDatabase(dbName);
        if (success) {
            m_db.logUserCommand(command);
            m_db.logUserCommand("Database dropped successfully");
            std::cout << "Database '" << dbName << "' dropped successfully." << std::endl;
        }
        else {
            m_db.logUserCommand("Failed to drop database");
            std::cout << "Failed to drop database '" << dbName << "'." << std::endl;
        }
        return true;
    }
    return false;
}

bool Command::parseUseDatabaseCommand(const std::string& command) {
    std::regex regex("^\\s*USE\\s+(\\w+)\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string dbName = match[1].str();
        m_db = Database::loadFromFile(dbName);
        std::cout << "Using database '" << dbName << "'." << std::endl;
        return true;
    }
    return false;
}

bool Command::parseCreateTableCommand(const std::string& command) {
    std::regex regex("^\\s*CREATE\\s+TABLE\\s+(\\w+)\\s*\\((.+)\\)\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string tableName = match[1].str();
        std::string columnDefinitionsStr = match[2].str();
        std::vector<std::string> columnDefinitions = splitColumnDefinitions(columnDefinitionsStr);

        std::vector<std::tuple<std::string, std::string, bool, bool, bool, std::string, std::string, std::string, std::string>> columns;
        std::string primaryKey;
        for (const auto& def : columnDefinitions) {
            std::regex columnRegex("^\\s*(\\w+)\\s+(\\w+)(\\s+PRIMARY\\s+KEY)?(\\s+NOT\\s+NULL)?(\\s+UNIQUE)?(\\s+DEFAULT\\s+([^,]+))?(\\s+CHECK\\s*\\((.+)\\))?\\s*$", std::regex::icase);
            std::smatch columnMatch;
            if (std::regex_match(def, columnMatch, columnRegex)) {
                std::string columnName = columnMatch[1].str();
                std::string columnType = columnMatch[2].str();
                bool isPrimaryKey = columnMatch[3].matched;
                bool isNotNull = columnMatch[4].matched;
                bool isUnique = columnMatch[5].matched;
                std::string defaultValue = columnMatch[7].str();
                std::string checkConstraint = columnMatch[9].str();

                std::string lowerBound, upperBound, maxLength;
                if (!checkConstraint.empty()) {
                    std::regex constraintRegex("^\\s*(\\w+)\\s*(>=|>)\\s*([^,\\s]+)\\s*(AND\\s+(\\w+)\\s*(<=|<)\\s*([^,\\s]+))?\\s*$", std::regex::icase);
                    std::smatch constraintMatch;
                    if (std::regex_match(checkConstraint, constraintMatch, constraintRegex)) {
                        lowerBound = constraintMatch[3].str();
                        if (constraintMatch[4].matched) {
                            upperBound = constraintMatch[7].str();
                        }
                    }
                    else {
                        std::regex lengthRegex("^\\s*LENGTH\\s*\\(\\s*(\\w+)\\s*\\)\\s*<=\\s*(\\d+)\\s*$", std::regex::icase);
                        if (std::regex_match(checkConstraint, constraintMatch, lengthRegex)) {
                            maxLength = constraintMatch[2].str();
                        }
                    }
                }

                columns.emplace_back(columnName, columnType, !isNotNull, isUnique, isPrimaryKey, defaultValue, lowerBound, upperBound, maxLength);
                if (isPrimaryKey) {
                    primaryKey = columnName;
                }
            }
        }

        bool success = m_db.createTable(tableName, columns, primaryKey);
        if (success) {
            m_db.logUserCommand(command);
            m_db.logUserCommand("Table created successfully");
            std::cout << "Table '" << tableName << "' created successfully." << std::endl;
        }
        else {
            m_db.logUserCommand("Failed to create table");
            std::cout << "Failed to create table '" << tableName << "'." << std::endl;
        }
        return true;
    }
    return false;
}

bool Command::parseDropTableCommand(const std::string& command) {
    std::regex regex("^\\s*DROP\\s+TABLE\\s+(\\w+)\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string tableName = match[1].str();
        bool success = m_db.dropTable(tableName);
        if (success) {
            m_db.logUserCommand(command);
            m_db.logUserCommand("Table dropped successfully");
            std::cout << "Table '" << tableName << "' dropped successfully." << std::endl;
        }
        else {
            m_db.logUserCommand("Failed to drop table");
            std::cout << "Failed to drop table '" << tableName << "'." << std::endl;
        }
        return true;
    }
    return false;
}

bool Command::parseInsertCommand(const std::string& command) {
    std::regex regex("^\\s*INSERT\\s+INTO\\s+(\\w+)\\s*\\((.+)\\)\\s*VALUES\\s*\\(([\\s\\S]+)\\)\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string tableName = match[1].str();
        std::string columnsStr = match[2].str();
        std::string valuesStr = match[3].str();

        std::vector<std::string> columns = splitCommand(columnsStr);
        std::vector<std::string> rows = splitRows(valuesStr);

        Table* table = m_db.getTable(tableName);
        if (table) {
            bool success = true;
            for (const std::string& row : rows) {
                std::vector<std::string> values = splitCommand(row);
                if (!table->insertRowFromStrings(columns, values)) {
                    success = false;
                    std::cout << "Failed to insert row: " << row << std::endl;
                    break;
                }
            }
            if (success) {
                m_db.saveTableToFile(tableName);
                m_db.logUserCommand(command);
                m_db.logUserCommand("Rows inserted successfully");
                std::cout << "Rows inserted successfully." << std::endl;
            }
            else {
                m_db.logUserCommand("Failed to insert rows");
                std::cout << "Failed to insert rows." << std::endl;
            }
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
        return true;
    }
    return false;
}

bool Command::parseSelectCommand(const std::string& command) {
    std::regex regex("^\\s*SELECT\\s+(.+)\\s+FROM\\s+(\\w+)(\\s+WHERE\\s+(.+))?\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string columnsStr = match[1].str();
        std::string tableName = match[2].str();
        std::string conditionsStr = match[4].str();

        std::vector<std::string> columns = splitCommand(columnsStr);
        std::vector<std::string> conditions = splitConditions(conditionsStr);

        Table* table = m_db.getTable(tableName);
        if (table) {
            std::vector<size_t> rowIDs;
            if (conditions.empty()) {
                rowIDs = table->getAllRowIDs();
            }
            else {
                for (const auto& condition : conditions) {
                    std::regex conditionRegex(R"(^\s*(\w+)\s*(=|!=|<>|<|>|<=|>=)\s*(.+?)\s*;?\s*$)", std::regex::icase);
                    std::smatch conditionMatch;
                    if (std::regex_match(condition, conditionMatch, conditionRegex)) {
                        std::string columnName = conditionMatch[1].str();
                        std::string op = conditionMatch[2].str();
                        std::string value = trim(conditionMatch[3].str());

                        if (op == "=") {
                            std::vector<size_t> equalRowIDs = table->getRowIDsByEqual(columnName, value);
                            rowIDs.insert(rowIDs.end(), equalRowIDs.begin(), equalRowIDs.end());
                        }
                        else if (op == "!=" || op == "<" || op == ">" || op == "<=" || op == ">=") {
                            std::vector<size_t> rangeRowIDs = table->getRowIDsByRange(columnName, op == ">" || op == ">=" ? value : "", op == "<" || op == "<=" ? value : "");
                            rowIDs.insert(rowIDs.end(), rangeRowIDs.begin(), rangeRowIDs.end());
                        }
                    }
                }
            }
            m_db.logUserCommand(command);
            std::vector<std::vector<Value>> values = table->getValues(rowIDs, columns);
            printValues(values);
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
        return true;
    }
    return false;
}

bool Command::parseUpdateCommand(const std::string& command) {
    std::regex regex("^\\s*UPDATE\\s+(\\w+)\\s+SET\\s+(.+?)\\s*(WHERE\\s+(.+?))?\\s*;?\\s*$", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string tableName = match[1].str();
        std::string setClauseStr = match[2].str();
        std::string conditionsStr = match.size() > 4 ? match[4].str() : "";


        std::vector<std::string> setClause = splitSetClause(setClauseStr);
        std::vector<std::string> conditions;
        if (!conditionsStr.empty()) {
            conditions = splitConditions(conditionsStr);
        }

        Table* table = m_db.getTable(tableName);
        if (table) {
            std::vector<size_t> rowIDs;
            if (conditions.empty()) {
                rowIDs = table->getAllRowIDs();
            }
            else {
                for (const auto& condition : conditions) {
                    std::regex conditionRegex(R"(^\s*(\w+)\s*(=|!=|<>|<|>|<=|>=)\s*(.+?)\s*$)", std::regex::icase);
                    std::smatch conditionMatch;
                    if (std::regex_match(condition, conditionMatch, conditionRegex)) {
                        std::string columnName = conditionMatch[1].str();
                        std::string op = conditionMatch[2].str();
                        std::string value = conditionMatch[3].str();
                        value = std::regex_replace(value, std::regex("\\s*;\\s*$"), "");

                        if (op == "=") {
                            std::vector<size_t> equalRowIDs = table->getRowIDsByEqual(columnName, value);
                            rowIDs.insert(rowIDs.end(), equalRowIDs.begin(), equalRowIDs.end());
                        }
                        else if (op == "!=" || op == "<" || op == ">" || op == "<=" || op == ">=") {
                            std::vector<size_t> rangeRowIDs = table->getRowIDsByRange(columnName, op == ">" || op == ">=" ? value : "", op == "<" || op == "<=" ? value : "");
                            rowIDs.insert(rowIDs.end(), rangeRowIDs.begin(), rangeRowIDs.end());
                        }
                    }
                }
            }

            std::vector<std::string> columnNames;
            std::vector<std::string> values;
            for (const auto& clause : setClause) {
                std::regex clauseRegex("^\\s*(\\w+)\\s*=\\s*(.+)\\s*$", std::regex::icase);
                std::smatch clauseMatch;
                if (std::regex_match(clause, clauseMatch, clauseRegex)) {
                    std::string columnName = clauseMatch[1].str();
                    std::string value = clauseMatch[2].str();
                    columnNames.push_back(columnName);
                    values.push_back(value);
                }
            }
            bool success = table->updateRows(rowIDs, columnNames, values);
            if (success) {
                m_db.saveTableToFile(tableName);
                m_db.logUserCommand(command);
                m_db.logUserCommand("Rows updated successfully");
                std::cout << "Rows updated successfully." << std::endl;
            }
            else {
                m_db.logUserCommand("Failed to update rows.");
                std::cout << "Failed to update rows." << std::endl;
            }
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
        return true;
    }
    return false;
}
bool Command::parseDeleteCommand(const std::string& command) {
    std::regex regex(R"(^\s*DELETE\s+FROM\s+(\w+)(\s+WHERE\s+(.+))?\s*;?\s*$)", std::regex::icase);
    std::smatch match;
    if (std::regex_match(command, match, regex)) {
        std::string tableName = match[1].str();
        std::string conditionsStr = match[3].str();
        std::vector<std::string> conditions = splitConditions(conditionsStr);

        Table* table = m_db.getTable(tableName);
        if (table) {
            std::vector<size_t> rowIDs;
            if (conditions.empty()) {
                rowIDs = table->getAllRowIDs();
            }
            else {
                for (const auto& condition : conditions) {
                    std::regex conditionRegex(R"(^\s*(\w+)\s*(=|!=|<>|<|>|<=|>=)\s*(.+?)\s*;?\s*$)", std::regex::icase);
                    std::smatch conditionMatch;
                    if (std::regex_match(condition, conditionMatch, conditionRegex)) {
                        std::string columnName = conditionMatch[1].str();
                        std::string op = conditionMatch[2].str();
                        std::string value = conditionMatch[3].str();

                        if (op == "=") {
                            std::vector<size_t> equalRowIDs = table->getRowIDsByEqual(columnName, value);
                            rowIDs.insert(rowIDs.end(), equalRowIDs.begin(), equalRowIDs.end());
                        }
                        else if (op == "!=" || op == "<" || op == ">" || op == "<=" || op == ">=") {
                            std::vector<size_t> rangeRowIDs = table->getRowIDsByRange(columnName, op == ">" || op == ">=" ? value : "", op == "<" || op == "<=" ? value : "");
                            rowIDs.insert(rowIDs.end(), rangeRowIDs.begin(), rangeRowIDs.end());
                        }
                    }
                }
            }

            bool success = table->deleteRows(rowIDs);
            if (success) {
                m_db.saveTableToFile(tableName);
                m_db.logUserCommand(command);
                m_db.logUserCommand("Rows deleted successfully");
                std::cout << "Rows deleted successfully." << std::endl;
            }
            else {
                m_db.logUserCommand("Failed to delete rows");
                std::cout << "Failed to delete rows." << std::endl;
            }
            return true;
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
    }
    return false;
}
bool Command::parseAlterTableCommand(const std::string& command) {
    std::regex addColumnRegex("^\\s*ALTER\\s+TABLE\\s+(\\w+)\\s+ADD\\s+COLUMN\\s+(\\w+)\\s+(\\w+)(\\s+NOT\\s+NULL)?(\\s+UNIQUE)?(\\s+DEFAULT\\s+([^,;]+))?(\s+CHECK\\s*\\((.+)\\))?\\s*;?\\s*$", std::regex::icase);
    std::smatch addColumnMatch;
    if (std::regex_match(command, addColumnMatch, addColumnRegex)) {
        std::string tableName = addColumnMatch[1].str();
        std::string columnName = addColumnMatch[2].str();
        std::string columnType = addColumnMatch[3].str();
        bool isNotNull = addColumnMatch[4].matched;
        bool isUnique = addColumnMatch[5].matched;
        std::string defaultValue = addColumnMatch[7].str();
        std::string checkConstraint = addColumnMatch[9].str();
        std::string lowerBound, upperBound, maxLength;
        if (!checkConstraint.empty()) {
            std::regex constraintRegex("^\\s*(\\w+)\\s*(>=|>)\\s*([^,\\s]+)\\s*(AND\\s+(\\w+)\\s*(<=|<)\\s*([^,\\s]+))?\\s*$", std::regex::icase);
            std::smatch constraintMatch;
            if (std::regex_match(checkConstraint, constraintMatch, constraintRegex)) {
                lowerBound = constraintMatch[3].str();
                if (constraintMatch[4].matched) {
                    upperBound = constraintMatch[7].str();
                }
            }
            else {
                std::regex lengthRegex("^\\s*LENGTH\\s*\\(\\s*(\\w+)\\s*\\)\\s*<=\\s*(\\d+)\\s*$", std::regex::icase);
                if (std::regex_match(checkConstraint, constraintMatch, lengthRegex)) {
                    maxLength = constraintMatch[2].str();
                }
            }
        }

        Table* table = m_db.getTable(tableName);
        if (table) {
            bool success = table->addColumn(columnName, columnType, !isNotNull, isUnique, false, defaultValue, lowerBound, upperBound, maxLength);
            if (success) {
                m_db.saveTableToFile(tableName);
                m_db.logUserCommand(command);
                m_db.logUserCommand("Column added successfully.");
                std::cout << "Column added successfully." << std::endl;
            }
            else {
                m_db.logUserCommand("Failed to add column.");
                std::cout << "Failed to add column." << std::endl;
            }
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
        return true;
    }

    std::regex dropColumnRegex("^\\s*ALTER\\s+TABLE\\s+(\\w+)\\s+DROP\\s+COLUMN\\s+(\\w+)\\s*;?\\s*$", std::regex::icase);
    std::smatch dropColumnMatch;
    if (std::regex_match(command, dropColumnMatch, dropColumnRegex)) {
        std::string tableName = dropColumnMatch[1].str();
        std::string columnName = dropColumnMatch[2].str();

        Table* table = m_db.getTable(tableName);
        if (table) {
            bool success = table->deleteColumns({ columnName });
            if (success) {
                m_db.saveTableToFile(tableName);
                m_db.logUserCommand(command);
                m_db.logUserCommand("Column dropped successfully.");
                std::cout << "Column dropped successfully." << std::endl;
            }
            else {
                m_db.logUserCommand("Failed to drop column.");
                std::cout << "Failed to drop column." << std::endl;
            }
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
        return true;
    }

    std::regex modifyColumnRegex("^\\s*ALTER\\s+TABLE\\s+(\\w+)\\s+MODIFY\\s+COLUMN\\s+(\\w+)(\\s+NOT\\s+NULL)?(\\s+UNIQUE)?(\\s+DEFAULT\\s+([^,]+))?(\\s+CHECK\\s*\\((.+)\\))?\\s*;?\\s*$", std::regex::icase);
    std::smatch modifyColumnMatch;
    if (std::regex_match(command, modifyColumnMatch, modifyColumnRegex)) {
        std::string tableName = modifyColumnMatch[1].str();
        std::string columnName = modifyColumnMatch[2].str();
        bool isNotNull = modifyColumnMatch[3].matched;
        bool isUnique = modifyColumnMatch[4].matched;
        std::string defaultValue = modifyColumnMatch[6].str();
        std::string checkConstraint = modifyColumnMatch[8].str();

        std::string lowerBound, upperBound, maxLength;
        if (!checkConstraint.empty()) {
            std::regex constraintRegex("^\\s*(\\w+)\\s*(>=|>)\\s*([^,\\s]+)\\s*(AND\\s+(\\w+)\\s*(<=|<)\\s*([^,\\s]+))?\\s*$", std::regex::icase);
            std::smatch constraintMatch;
            if (std::regex_match(checkConstraint, constraintMatch, constraintRegex)) {
                lowerBound = constraintMatch[3].str();
                if (constraintMatch[4].matched) {
                    upperBound = constraintMatch[7].str();
                }
            }
            else {
                std::regex lengthRegex("^\\s*LENGTH\\s*\\(\\s*(\\w+)\\s*\\)\\s*<=\\s*(\\d+)\\s*$", std::regex::icase);
                if (std::regex_match(checkConstraint, constraintMatch, lengthRegex)) {
                    maxLength = constraintMatch[2].str();
                }
            }
        }

        Table* table = m_db.getTable(tableName);
        if (table) {
            bool success = table->modifyColumn(columnName, !isNotNull, isUnique, false, defaultValue, lowerBound, upperBound, maxLength);
            if (success) {
                m_db.saveTableToFile(tableName);
                m_db.logUserCommand(command);
                m_db.logUserCommand("Column modified successfully.");
                std::cout << "Column modified successfully." << std::endl;
            }
            else {
                m_db.logUserCommand("Failed to modify column.");
                std::cout << "Failed to modify column." << std::endl;
            }
        }
        else {
            std::cout << "Table '" << tableName << "' does not exist." << std::endl;
        }
        return true;
    }

    return false;
}
std::vector<std::string> Command::splitCommand(const std::string& command) {
    std::vector<std::string> result;
    std::istringstream iss(command);
    std::string token;
    while (std::getline(iss, token, ',')) {
        result.push_back(trim(token));
    }
    return result;
}
std::vector<std::string> Command::splitColumnDefinitions(const std::string& columnDefinitions) {
    std::vector<std::string> result;
    std::istringstream iss(columnDefinitions);
    std::string token;
    while (std::getline(iss, token, ',')) {
        result.push_back(trim(token));
    }
    return result;
}
std::vector<std::string> Command::splitConditions(const std::string& conditions) {
    std::vector<std::string> result;
    std::istringstream iss(conditions);
    std::string token; while (std::getline(iss, token, ',')) {
        result.push_back(trim(token));
    }
    return result;
}
std::vector<std::string> Command::splitSetClause(const std::string& setClause) {
    std::vector<std::string> result;
    std::istringstream iss(setClause);
    std::string token; while (std::getline(iss, token, ',')) {
        result.push_back(trim(token));
    }
    return result;
}
std::string Command::trim(const std::string& str) {
    std::string result = str;
    result.erase(0, result.find_first_not_of(" \t\n\r"));
    result.erase(result.find_last_not_of(" \t\n\r") + 1);
    if ((result.front() == '\'' && result.back() == '\'') || (result.front() == '\"' && result.back() == '\"')) {
        result = result.substr(1, result.length() - 2);
    }
    return result;
}
std::vector<std::string> Command::splitRows(const std::string& rowsStr) {
    std::vector<std::string> rows;
    std::string row;
    for (char ch : rowsStr) {
        if (ch == '(') {
            if (!row.empty()) {
                row = trim(row);
                if (!row.empty()) {
                    rows.push_back(row);
                }
                row.clear();
            }
        }
        else if (ch == ')') {
            row = trim(row);
            if (!row.empty()) {
                rows.push_back(row);
            }
            row.clear();
        }
        else {
            row += ch;
        }
    }
    if (!row.empty()) {
        row = trim(row);
        if (!row.empty()) {
            rows.push_back(row);
        }
    }
    return rows;
}

std::string Command::readMultilineCommand() {
    std::string command;
    std::string line;
    while (std::getline(std::cin, line)) {
        command += line;
        if (line.find(';') != std::string::npos) {
            break;
        }
    }

    command.erase(std::remove(command.begin(), command.end(), '\n'), command.end());

    return command;
}
